/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package uk.ac.bath.environment;


import java.util.Vector;
import uk.ac.bath.Logger;
import uk.ac.bath.util.Tweakable;

/**
 *   Generic wrapper for experiements
 * 
 *   Combines an Evaluator (problem domain)
 *   with a Scheme (search stratagy)
 *
 *   You can also register a GuiUpdater to monitor the progress.
 *
 *
 * @author pjl
 */
final public class PlugEnvironment implements EnvironmentIF {

    protected Vector<Tweakable> tweaks = new Vector<Tweakable>();
    GuiUpdaterIF guiUpdater;
    private long millisPerFrame;
    protected int nTick;
    private Thread runThread;
    private boolean dispose = false;
    private boolean doZap;
    protected Evaluator evaluator;
    boolean done = false;
    private Scheme scheme;
    private Logger logger;
    boolean stopOnSuccess;

    public PlugEnvironment(Evaluator eval, Scheme scheme) {
        this.evaluator = eval;
        this.scheme = scheme;
        tweaks.addAll(eval.getTweaks());
    }

    public final int getTickCount() {
        return nTick;
    }

    public Vector<Tweakable> getTweaks() {
        return tweaks;
    }

    /**
     *  called every evaluation to update the display.
     */
    private final void notifyMaybe() {
        if (guiUpdater != null) {
            guiUpdater.myUpdate();
        }
        if (millisPerFrame > 0) {
            try {
                Thread.sleep(millisPerFrame);
            } catch (InterruptedException e) {
            }
        }
    }

    public final void run() {
        doZap = true;
        run(-1);
    }

    public void run(int nStep) {

        runThread = Thread.currentThread();

        while (nStep != 0) {
            if (dispose) {
                runThread = null;
                guiUpdater.dispose();
                guiUpdater = null;
                return;
            }
            boolean success=tick();
            notifyMaybe();
            if ((logger != null) & success) return;         
            if (nStep > 0) {
                nStep--;
            }
            nTick++;
        }
    }

    public void setFrameRate(long millisPerFrame) {
        //    System.out.println(" Set frame rate " + millisPerFrame);
        this.millisPerFrame = millisPerFrame;
        if (runThread == null) {
            return;
        }
        runThread.interrupt();
    }

    /**
     *
     * @param guiUpdater obeject repsonsible for displaying the state
     */
    public void setGuiUpdater(GuiUpdaterIF guiUpdater) {
        this.guiUpdater = guiUpdater;
    }

    protected boolean tick() {

        if (doZap) {
            evaluator.init();
            scheme.init();
            doZap = false;
            MachineIF m = scheme.nextMachine();
            evaluator.setMachine(m);
        }

        Fitness fitness = evaluator.tick();

        if (fitness.decided()) {
            if (fitness.success()) {  // found the answer  just free wheel on the solution
                if (logger != null) {
                    logger.print(scheme.getStatus()+evaluator.getStatus());                   
                }
                return true;
            }
            scheme.endOfEvaluation(fitness); // ness(fit.fitness);
            if ( logger != null) logger.print(scheme.getStatus()+" "+ evaluator.getStatus());
            MachineIF m = scheme.nextMachine();
            evaluator.setMachine(m);
        }
        return false;
    }

    public void dispose() {
        dispose = true;
    }

    /**
     *
     *  resets everything so you start a fresh search.
     *
     */
    public void zap() {
        doZap = true;
    }

    @Override
    public View createSimulationView() {
        return evaluator.createView();
    }

    public void setLogger(Logger log) {
        this.logger=log;
    }

    public String reportSetup() {
        return evaluator.reportSetup() +"\n"+ scheme.reportSetup();
    }
}
